---
title: Credentials
---

import { Steps, Callout } from "nextra/components"
import { Code } from "@/components/Code"

# Credentials

To setup Auth.js with any external authentication mechanisms or use a traditional username/email and password flow, we can use the `Credentials` provider. This provider is designed to forward any credentials inserted into the login form (i.e. username/password, but not limited to) to your authentication service.

<Callout type="warning">
  The industry has come a long way since usernames and passwords
  as the go-to mechanism for authenticating and authorizing users to
  web applications. Therefore, if possible, we recommend a more modern and
  secure authentication mechanism such as any of the [OAuth
  providers](/getting-started/authentication/oauth), [Email Magic
  Links](/getting-started/authentication/email), or [WebAuthn
  (Passkeys)](/getting-started/authentication/webauthn) options instead.

However, we also want to be flexible and support anything
you deem appropriate for your application and use case,
so there are no plans to remove this provider.

</Callout>

<Callout>
  By default, the Credentials provider does not persist data in the database.
  However, you can still create and save any data in your database, you just
  have to provide the necessary logic, eg. to encrypt passwords, add
  rate-limiting, add password reset functionality, etc.
</Callout>

<Steps>

### Credentials Provider

First, let's initialize the `Credentials` provider in the Auth.js configuration file. You'll have to import the provider and add it to your `providers` array.

<Code>
<Code.Next>

```ts filename="./auth.ts" {2, 8}
import NextAuth from "next-auth"
import Credentials from "next-auth/providers/credentials"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"

export const { handlers, signIn, signOut, auth } = NextAuth({
  providers: [
    Credentials({
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        let user = null

        // logic to salt and hash password
        const pwHash = saltAndHashPassword(credentials.password)

        // logic to verify if the user exists
        user = await getUserFromDb(credentials.email, pwHash)

        if (!user) {
          // No user found, so this is their first attempt to login
          // Optionally, this is also the place you could do a user registration
          throw new Error("Invalid credentials.")
        }

        // return user object with their profile data
        return user
      },
    }),
  ],
})
```

</Code.Next>
<Code.Qwik>
  
```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Credentials from "@auth/qwik/providers/credentials"

export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
  () => ({
    providers: [
      Credentials({
        credentials: {
          email: { label: "Email" },
          password: { label: "Password", type: "password" },
        },
        async authorize(credentials) {
          const response = await getUserFromDb(credentials)
          if (!response.ok) return null
          return (await response.json()) ?? null
        },
      }),
    ],
  })
)
```

</Code.Qwik>
<Code.Svelte>

```ts filename="./src/auth.ts" {2, 8}
import { SvelteKitAuth } from "@auth/sveltekit"
import Credentials from "@auth/sveltekit/providers/credentials"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"

export const { signIn, signOut, handle } = SvelteKitAuth({
  providers: [
    Credentials({
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        let user = null

        // logic to salt and hash password
        const pwHash = saltAndHashPassword(credentials.password)

        // logic to verify if user exists
        user = await getUserFromDb(credentials.email, pwHash)

        if (!user) {
          // No user found, so this is their first attempt to login
          // Optionally, this is also the place you could do a user registration
          throw new Error("Invalid credentials.")
        }

        // return JSON object with the user data
        return user
      },
    }),
  ],
})
```

Don't forget to re-export the `handle` in your `./src/hooks.server.ts` file.

```ts filename="./src/hooks.server.ts"
export { handle } from "./auth"
```

</Code.Svelte>

<Code.Express>

```ts filename="./src/routes/auth.route.ts" {2, 11}
import { ExpressAuth } from "@auth/express"
import Credentials from "@auth/express/providers/credentials"
import express from "express"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"

const app = express()
app.use(
  "/auth/*",
  ExpressAuth({
    providers: [
      Credentials({
        // You can specify which fields should be submitted, by adding keys to the `credentials` object.
        // e.g. domain, username, password, 2FA token, etc.
        credentials: {
          email: {},
          password: {},
        },
        authorize: async (credentials) => {
          let user = null

          // logic to salt and hash password
          const pwHash = saltAndHashPassword(credentials.password)

          // logic to verify if the user exists
          user = await getUserFromDb(credentials.email, pwHash)

          if (!user) {
            // No user found, so this is their first attempt to login
            // Optionally, this is also the place you could do a user registration
            throw new Error("Invalid credentials.")
          }

          // return user object with the their profile data
          return user
        },
      }),
    ],
  })
)
```

</Code.Express>
</Code>

<Callout type="info">
  If you're using TypeScript, you can [augment the `User`
  interface](/getting-started/typescript#module-augmentation) to match the
  response of your `authorize` callback, so whenever you read the user in other
  callbacks (like the `jwt`) the type will match correctly.
</Callout>

### Signin Form

Finally, let's create a simple sign-in form.

<Code>
<Code.Next>

```tsx filename="./components/sign-in.tsx" {13, 17} /formData/
import { signIn } from "@/auth"

export function SignIn() {
  return (
    <form
      action={async (formData) => {
        "use server"
        await signIn("credentials", formData)
      }}
    >
      <label>
        Email
        <input name="email" type="email" />
      </label>
      <label>
        Password
        <input name="password" type="password" />
      </label>
      <button>Sign In</button>
    </form>
  )
}
```

</Code.Next>
<Code.NextClient>

```tsx filename="./components/sign-in.tsx"
"use client"
import { signIn } from "next-auth/react"

export function SignIn() {
  const credentialsAction = (formData: FormData) => {
    signIn("credentials", formData)
  }

  return (
    <form action={credentialsAction}>
      <label htmlFor="credentials-email">
        Email
        <input type="email" id="credentials-email" name="email" />
      </label>
      <label htmlFor="credentials-password">
        Password
        <input type="password" id="credentials-password" name="password" />
      </label>
      <input type="submit" value="Sign In" />
    </form>
  )
}
```

</Code.NextClient>
<Code.Qwik>

```ts filename="/src/routes/index.tsx"
import { component$ } from "@builder.io/qwik"
import { Form } from "@builder.io/qwik-city"
import { useSignIn } from "./plugin@auth"

export default component$(() => {
  const signInSig = useSignIn()

  return (
    <Form action={signInSig}>
      <label>
        Email
        <input name="email" type="email" />
      </label>
      <label>
        Password
        <input name="password" type="password" />
      </label>
      <button>Sign In</button>
    </Form>
  )
})
```

</Code.Qwik>
<Code.Svelte>

```svelte filename="src/route/+page.svelte" /bind:value/ /signIn/
<script>
  import { signIn } from "../auth"
  import { page } from "$app/stores"

  let email = ""
  let password = ""
</script>

<div>
  <form>
    <label>
      Email
      <input name="email" type="email" bind:value={email} />
    </label>
    <label>
      Password
      <input name="password" type="password" bind:value={password} />
    </label>
    <button on:click={() => signIn("credentials", { email, password })}>
      Log in
    </button>
  </form>
</div>
```

</Code.Svelte>

<Code.Express>

```html filename="views/signin.html"
<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Sign In</title>
  </head>
  <body>
    <h1>Sign In</h1>
    <form action="/auth/signin" method="POST">
      <label for="email">Email:</label>
      <input type="email" name="email" id="email" required />
      <br />
      <label for="password">Password:</label>
      <input type="password" name="password" id="password" required />
      <br />
      <button type="submit">Sign In</button>
    </form>
  </body>
</html>
```

</Code.Express>

</Code>

</Steps>

## Validating credentials

Always validate the credentials server-side, i.e. by leveraging a schema validation library like [Zod](https://zod.dev).

```bash npm2yarn
npm install zod
```

Next, we'll set up the schema and parsing in our `auth.ts` configuration file, using the `authorize` callback on the `Credentials` provider.

<Code>
<Code.Next>

```ts filename="./lib/zod.ts"
import { object, string } from "zod"

export const signInSchema = object({
  email: string({ required_error: "Email is required" })
    .min(1, "Email is required")
    .email("Invalid email"),
  password: string({ required_error: "Password is required" })
    .min(1, "Password is required")
    .min(8, "Password must be more than 8 characters")
    .max(32, "Password must be less than 32 characters"),
})
```

```ts filename="./auth.ts" {22}
import NextAuth from "next-auth"
import { ZodError } from "zod"
import Credentials from "next-auth/providers/credentials"
import { signInSchema } from "./lib/zod"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"
import { getUserFromDb } from "@/utils/db"

export const { handlers, auth } = NextAuth({
  providers: [
    Credentials({
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        try {
          let user = null

          const { email, password } = await signInSchema.parseAsync(credentials)

          // logic to salt and hash password
          const pwHash = saltAndHashPassword(password)

          // logic to verify if the user exists
          user = await getUserFromDb(email, pwHash)

          if (!user) {
            throw new Error("Invalid credentials.")
          }

          // return JSON object with the user data
          return user
        } catch (error) {
          if (error instanceof ZodError) {
            // Return `null` to indicate that the credentials are invalid
            return null
          }
        }
      },
    }),
  ],
})
```

</Code.Next>
<Code.Qwik>

```ts filename="./lib/zod.ts"
import { object, string } from "zod"

export const signInSchema = object({
  email: string({ required_error: "Email is required" })
    .min(1, "Email is required")
    .email("Invalid email"),
  password: string({ required_error: "Password is required" })
    .min(1, "Password is required")
    .min(8, "Password must be more than 8 characters")
    .max(32, "Password must be less than 32 characters"),
})
```

```ts filename="/src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik"
import Credentials from "@auth/qwik/providers/credentials"
import { signInSchema } from "./lib/zod"

export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
  () => ({
    providers: [
      Credentials({
        credentials: {
          email: { label: "Email" },
          password: { label: "Password", type: "password" },
        },
        async authorize(credentials) {
          const { email, password } = await signInSchema.parseAsync(credentials)

          // Your logic here
        },
      }),
    ],
  })
)
```

</Code.Qwik>
<Code.Svelte>

```ts filename="./lib/zod.ts"
import { object, string } from "zod"

export const signInSchema = object({
  email: string({ required_error: "Email is required" })
    .min(1, "Email is required")
    .email("Invalid email"),
  password: string({ required_error: "Password is required" })
    .min(1, "Password is required")
    .min(8, "Password must be more than 8 characters")
    .max(32, "Password must be less than 32 characters"),
})
```

```ts filename="./auth.ts" {22-23}
import SvelteKitAuth from "@auth/sveltekit"
import { ZodError } from "zod"
import Credentials from "@auth/sveltekit/providers/credentials"
import { signInSchema } from "./lib/zod"
// Your own logic for dealing with plaintext password strings; be careful!
import { saltAndHashPassword } from "@/utils/password"
import { getUserFromDb } from "@/utils/db"

export const { handle } = SvelteKitAuth({
  providers: [
    Credentials({
      // You can specify which fields should be submitted, by adding keys to the `credentials` object.
      // e.g. domain, username, password, 2FA token, etc.
      credentials: {
        email: {},
        password: {},
      },
      authorize: async (credentials) => {
        try {
          let user = null

          const { email, password } =
            await createUserSchema.parseAsync(credentials)

          // logic to salt and hash password
          const pwHash = saltAndHashPassword(password)

          // logic to verify if the user exists
          user = await getUserFromDb(email, pwHash)

          if (!user) {
            throw new Error("Invalid credentials.")
          }

          // return JSON object with the user data
          return user
        } catch (error) {
          if (error instanceof ZodError) {
            // Return `null` to indicate that the credentials are invalid
            return null
          }
        }
      },
    }),
  ],
})
```

</Code.Svelte>
</Code>
